﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Xml.Linq;
using Opds_Viewer.Properties;
using Opds_Viewer.Utils;

namespace Opds_Viewer.Model
{
    /// <summary>
    /// Class describing a opds catalog in my contacts
    /// </summary>
    public class MyCatalog
    {
        #region properties

        /// <summary>
        /// The id of the contact.
        /// </summary>
        public int Id { get; set; }

        /// <summary>
        /// The name of the contact catalog.
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// The url of the index of the opds catalog.
        /// </summary>
        public string UrlIndex { get; set; }

        /// <summary>
        /// The url of the metadata.db.
        /// </summary>
        public string UrlDb { get; set; }

        /// <summary>
        /// The url of the metadata.db on the hard drive.
        /// </summary>
        public string FileDb { get; set; }

        /// <summary>
        /// The status of the catalog (false for unreachable).
        /// </summary>
        public bool Status { get; set; }

        /// <summary>
        /// The date of the last time the catalog was updated.
        /// </summary>
        public DateTime DateMaj { get; set; }

        /// <summary>
        /// The number of books of the catalog.
        /// </summary>
        public int NbBooks { get; set; }
        
        /// <summary>
        /// The length of the index for the last maj.
        /// </summary>
        public string LastLength { get; set; }

        /// <summary>
        /// Says if we have to update or not the catalog db.
        /// </summary>
        public bool ToUpdate { get; set; }

        #endregion

        #region constructor

        /// <summary>
        /// Create an empty MyCatalog
        /// </summary>
        public MyCatalog()
        {
        }

        /// <summary>
        /// Construct a new MyCatlog based on a xml node.
        /// </summary>
        /// <param name="xCatalog">The xml node.</param>
        public MyCatalog(XElement xCatalog)
        {
            var attribute = xCatalog.Attribute(XmlCatNode.Id);
            if (attribute != null) this.Id = Int32.Parse(attribute.Value);

            var element = xCatalog.Element(XmlCatNode.Name); this.Name = element != null ? element.Value : "NaN";
            element = xCatalog.Element(XmlCatNode.Url); this.UrlIndex = element != null ? element.Value : string.Empty;
            element = xCatalog.Element(XmlCatNode.FileDb); this.FileDb = element != null ? element.Value : string.Empty;
            element = xCatalog.Element(XmlCatNode.LastLength); this.LastLength = element != null ? element.Value : string.Empty;
            element = xCatalog.Element(XmlCatNode.NbBooks); this.NbBooks = element != null ? Int32.Parse(element.Value) : 0;
            element = xCatalog.Element(XmlCatNode.LastUpdate);
            this.DateMaj = DateTime.MinValue;
            if (element != null && !string.IsNullOrEmpty(element.Value))
            {
                this.DateMaj = DateTime.Parse(element.Value);
            }

            // Construct the url of the database
            if (!string.IsNullOrEmpty(this.UrlIndex))
            {
                // Location of the db always 2 levels upper than than the index/catalog
                var i = this.UrlIndex.LastIndexOf('/', this.UrlIndex.LastIndexOf('/') - 1);
                this.UrlDb = this.UrlIndex.Substring(0, i + 1) + Settings.Default.NameDb;
            }

            // To avoid any error after, we check the index to know if it's reachable or not (404)
            this.Status = this.IsAvailable();

            // If the file doesn't exists, we have to download it
            if (!ToUpdate && !File.Exists(FileDb))
            {
                ToUpdate = true;
            }
        }

        #endregion

        #region public functions

        /// <summary>
        /// Saves this instance into the config file.
        /// </summary>
        public void Save()
        {
            var doc = XDocument.Load(Settings.Default.XmlCatalogs);
            var root = doc.Element(XmlCatNode.Root);
            if (root != null)
            {
                // Search the element with the correspondant id
                // ReSharper disable PossibleNullReferenceException
                var element = root.Elements(XmlCatNode.Catalog).Where(e => e.Attribute(XmlCatNode.Id).Value == this.Id.ToString()).First();

                element.Element(XmlCatNode.Name).Value = this.Name;
                element.Element(XmlCatNode.Url).Value = this.UrlIndex;
                element.Element(XmlCatNode.FileDb).Value = this.FileDb;
                element.Element(XmlCatNode.LastUpdate).Value = this.DateMaj.ToShortDateString();
                element.Element(XmlCatNode.LastLength).Value = this.LastLength;
                element.Element(XmlCatNode.NbBooks).Value = this.NbBooks.ToString();
                // ReSharper restore PossibleNullReferenceException

                doc.Save(Settings.Default.XmlCatalogs);
            }
        }

        #endregion

        #region private functions

        /// <summary>
        /// Determines whether this catalog is available.
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if this catalog is available; otherwise, <c>false</c>.
        /// </returns>
        private bool IsAvailable()
        {
            try
            {
                using (var client = new MyClient())
                {
                    client.HeadOnly = true;
                    client.DownloadData(this.UrlIndex.Replace("xml","html"));
                    var length = client.ResponseHeaders["content-length"];
                    var date = DateTime.Parse(client.ResponseHeaders["date"]);

                    //TODO : see how works date in headers to check updates
                    //if (this.DateMaj.Date < date.Date || (this.DateMaj.Date==date.Date && this.LastLength != length))
                    if (this.LastLength != length)
                    {
                        this.ToUpdate = true;
                        this.LastLength = length;
                    }
                }
                /*var req = (HttpWebRequest)WebRequest.Create(this.UrlIndex);
                var resp = (HttpWebResponse)req.GetResponse();
                if (this.DateMaj.Date < resp.LastModified.Date || (this.DateMaj.Date == resp.LastModified.Date && this.LastLength != resp.ContentLength.ToString()))
                {
                    this.ToUpdate = true;
                    this.LastLength = resp.ContentLength.ToString();
                }*/
                return true;
            }
            catch (WebException)
            {
                return false;
            }
        }

        #endregion
    }
}
